package team2.game.crow;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.anddev.andengine.engine.handler.IUpdateHandler;
import org.anddev.andengine.engine.handler.timer.ITimerCallback;
import org.anddev.andengine.engine.handler.timer.TimerHandler;
import org.anddev.andengine.entity.Entity;
import org.anddev.andengine.entity.sprite.AnimatedSprite;
import org.anddev.andengine.util.Debug;
import org.andengine.shmup.Enemy;
import org.andengine.shmup.EnemyPool;

public class CollisionHandler implements IUpdateHandler {

	// ===========================================================
	// Constants
	// ===========================================================
	private static final int LEFTBOUNDARY = 0;
	private static final int RIGHTBOUNDARY = 480;
	private static final int UPPERBOUNDARY = 0;
	private static final int LOWERBOUNDARY = 720;
	
	// ===========================================================
	// Fields
	// ===========================================================
	
	private List<Laser> bullets;
	private ArrayList<Laser> bulletsToRemove;
	private List<Laser> enemyBullets;
	private ArrayList<Laser> enemyBulletsToRemove;
	private ArrayList<Laser> wings;
	private ArrayList<Laser> wingsToRemove;
	private List<Enemy> enemies;
	private ArrayList<Enemy> enemiesToRemove;
	private ArrayList<PowerUp> powerUps;
	private ArrayList<PowerUp> powerUpsToRemove;
	private ArrayList<HitSpark> explosions;
	private ArrayList<HitSpark> explosionsToRemove;
	private Ship player;
	private EnemyPool bladerPool;
	private EnemyPool gruntPool;
	private SpritePool lPool;
	private SpritePool enemyBulletPool;
	private SpritePool wPool;
	private CrowActivity main;
	protected Random rng;

	// ===========================================================
	// Constructors
	// ===========================================================
	
	public CollisionHandler(final Ship player, final CrowActivity main){
		this.bullets = main.shots;
		this.bulletsToRemove = new ArrayList<Laser>();
		this.enemies = main.enemies;
		this.enemiesToRemove = new ArrayList<Enemy>();
		this.powerUps = main.powerUps;
		this.powerUpsToRemove = new ArrayList<PowerUp>();
		this.wings = player.munnins;
		this.wingsToRemove = new ArrayList<Laser>();
		this.player = player;
		this.bladerPool = main.bladerPool;
		this.gruntPool = main.gruntPool;
		this.lPool = main.lPool;
		this.enemyBulletPool = main.enemyLaserPool;
		this.wPool = player.mPool;
		this.main = main;
		this.rng = new Random();
		this.enemyBullets = main.enemyShots;
		this.enemyBulletsToRemove = new ArrayList<Laser>();
		this.explosions = new ArrayList<HitSpark>();
		this.explosionsToRemove = new ArrayList<HitSpark>();
	}
	
	// ===========================================================
	// Getter & Setter
	// ===========================================================

	// ===========================================================
	// Methods for/from SuperClass/Interfaces
	// ===========================================================
	@Override
	public void onUpdate(float pSecondsElapsed) {
		//Collision for the lasers fired by the ship
		if(player.getLaserLevel() < 3){
			for(Laser check: bullets){
				if(check.getY() < UPPERBOUNDARY | check.getX() < LEFTBOUNDARY | check.getX() > RIGHTBOUNDARY){
					lPool.recyclePoolItem(check);
					bulletsToRemove.add(check);
				}
				else if((!(main.currentBoss == null)) && check.collidesWith(main.bossWeakPoint) && main.currentBoss.isAlive && !main.currentBoss.isInvincible){
					if(main.currentBoss.isAlive && !main.currentBoss.isEntering){
						main.currentBoss.setHP(5);
						spawnSpark(check.getX(), check.getY());
						Debug.d("Hit the boss");
						if(!main.currentBoss.isAlive){
							bossExplosion();
							main.currentBoss.isAlive = false;
							Debug.d("Boss Died");
							main.score += 10000;
							main.scoreText.setText("Score: "+main.score);
							//main.currentBoss.setVisible(false);
							for(AnimatedSprite las : main.currentBoss.lasers){
								las.setVisible(false);
							}
						}
					}
					else{
						Debug.d("Fake Hit");
					}
					lPool.recyclePoolItem(check);
					bulletsToRemove.add(check);
				}
				else for(Enemy target: enemies){
					if(check.collidesWith(target)){
						if(target.isAlive){
							target.setHP(40);
							spawnSpark(check.getX(), check.getY());
							if(!target.isAlive){
								main.score += 100;
								main.scoreText.setText("Score: "+main.score);
								recycleEnemy(target);
								enemiesToRemove.add(target);
								spawnPowerUp(target.getCenterX(),target.getCenterY());
								spawnSpark(check.getX(), check.getY());
							}
							lPool.recyclePoolItem(check);
							bulletsToRemove.add(check);
						}
					}
				}
			}
		}
		else{//Super Laser collision
			AnimatedSprite check = player.superLaser;
			for(Enemy target: enemies){
				if(check.collidesWith(target)){
					if(target.isAlive){
						target.setHP(60);
						spawnSpark(target.getCenterX(),target.getCenterY());
						if(!target.isAlive){
							main.score += 200;
							main.scoreText.setText("Score: "+main.score);
							recycleEnemy(target);
							enemiesToRemove.add(target);
							spawnPowerUp(target.getCenterX(),target.getCenterY());
							spawnSpark(target.getCenterX(),target.getCenterY());
						}
					}
				}
			}
			if((!(main.currentBoss == null)) && check.collidesWith(main.bossWeakPoint) && main.currentBoss.isAlive && !main.currentBoss.isInvincible){
				main.currentBoss.setHP((int)(100*pSecondsElapsed));
				Debug.d("Hit the boss");
				if(!main.currentBoss.isAlive){
					Debug.d("Boss Died");
					bossExplosion();
					main.currentBoss.isAlive = false;
					main.score += 20000;
					main.scoreText.setText("Score: "+main.score);
					//main.currentBoss.setVisible(false);
					for(AnimatedSprite las : main.currentBoss.lasers){
						las.setVisible(false);
					}
				}
			}
		}
		bullets.removeAll(bulletsToRemove);
		bulletsToRemove.clear();
		enemies.removeAll(enemiesToRemove);
		enemiesToRemove.clear();
		
		//The wings from the teleport colliding with enemies
		for(Laser check: wings){
			if(check.getY() < UPPERBOUNDARY | check.getX() < LEFTBOUNDARY | check.getX() > RIGHTBOUNDARY){
				wPool.recyclePoolItem(check);
				wingsToRemove.add(check);
			}
			else if((!(main.currentBoss == null)) && check.collidesWith(main.bossWeakPoint)){
				if(main.currentBoss.isAlive){
					main.currentBoss.setHP(50);
					spawnSpark(check.getX(), check.getY());
					if(!main.currentBoss.isAlive){
						bossExplosion();
						main.currentBoss.isAlive = false;
						Debug.d("Boss Died");
						main.score += 30000;
						main.scoreText.setText("Score: "+main.score);
						//main.currentBoss.setVisible(false);
						for(AnimatedSprite las : main.currentBoss.lasers){
							las.setVisible(false);
						}
					}
				}
				wPool.recyclePoolItem(check);
				wingsToRemove.add(check);
			}
			else for(Enemy target: enemies){
				if(check.collidesWith(target)){
					if(target.isAlive){
						target.setHP(80);
						spawnSpark(check.getX(), check.getY());
						if(!target.isAlive){
							main.score += 300;
							main.scoreText.setText("Score: "+main.score);
							recycleEnemy(target);
							enemiesToRemove.add(target);
							spawnPowerUp(target.getCenterX(),target.getCenterY());
							spawnSpark(target.getCenterX(),target.getCenterY());
						}
						wPool.recyclePoolItem(check);
						wingsToRemove.add(check);
					}
				}
			}
		}
		wings.removeAll(wingsToRemove);
		wingsToRemove.clear();
		enemies.removeAll(enemiesToRemove);
		enemiesToRemove.clear();
		
		//Enemy colliding with the player
		for(Enemy check: enemies){			
			if(check.collidesWith(player) && !player.isInvulnerable()){
				spawnSpark(check.getCenterX(),check.getCenterY());
				if(player.takeDamage(30)){
					//Game Over
					main.gameOver();
				}
				Debug.d("Player Health: "+player.getHealth());
				recycleEnemy(check);
				enemiesToRemove.add(check);
			}
		}
		enemies.removeAll(enemiesToRemove);
		enemiesToRemove.clear();
		
		//Enemy Leaving game bounds
		for(Enemy check: enemies){			
			if(check.getY() > LOWERBOUNDARY){
				recycleEnemy(check);
				enemiesToRemove.add(check);
			}
		}
		enemies.removeAll(enemiesToRemove);
		enemiesToRemove.clear();
		
		//Enemy Bullets Colliding with Player
		for(Laser check: enemyBullets){
			if(check.getY() < UPPERBOUNDARY | check.getX() < LEFTBOUNDARY | check.getX() > RIGHTBOUNDARY | check.getY() > LOWERBOUNDARY){
				enemyBulletPool.recyclePoolItem(check);
				enemyBulletsToRemove.add(check);
			}
			if(check.collidesWith(player) && !player.isInvulnerable()){
				spawnSpark(check.getX(),check.getY());
				if(player.takeDamage(30)){
					//Game Over game over Scene...
					main.gameOver();
				}
				Debug.d("Player Health: "+player.getHealth());
				enemyBulletPool.recyclePoolItem(check);
				enemyBulletsToRemove.add(check);
			}
		}
		enemyBullets.removeAll(enemyBulletsToRemove);
		enemyBulletsToRemove.clear();
		
		//Boss
		/*
		if(!(main.currentBoss == null))
		if(main.currentBoss.isAlive){
			if(player.collidesWith(main.currentBoss)){
				if(player.takeDamage(50))
					main.getEngine().stop();
			}
		}
		*/
		
		//Boss Super Laser
		if(!(main.currentBoss == null))
		if(main.currentBoss.isAlive){
			for(AnimatedSprite check: main.currentBoss.lasers){
				if(check.isVisible() && player.collidesWith(check)){
					if(player.takeDamage(100)){
						//Should go to a game over scene...
						main.gameOver();
					}
				}
			}
		}
		
		
		//Power up colliding with the ship
		for(final PowerUp check: powerUps){
			if(check.collidesWith(player)){
				player.usePowerUp(check);
				powerUpsToRemove.add(check);
				removeEntity(check);
			}
			if(check.getY() > LOWERBOUNDARY){
				powerUpsToRemove.add(check);
				removeEntity(check);
			}
		}
		powerUps.removeAll(powerUpsToRemove);
		powerUpsToRemove.clear();
		
		//Clear explosions
		for(final HitSpark spark: explosions){
			if(spark.expired){
				explosionsToRemove.add(spark);
				main.hitSparks.recyclePoolItem(spark);
			}
		}
		explosions.removeAll(explosionsToRemove);
		explosionsToRemove.clear();
	}

	@Override
	public void reset() {
		// TODO Auto-generated method stub
		
	}
	// ===========================================================
	// Methods
	// ===========================================================
	
	private void spawnPowerUp(float x, float y){
		int chance = rng.nextInt(100);
		if(chance <= 10){
			PowerUp temp = new PowerUp(x, y, main.laserUp,CrowEnum.LASERUP);
			temp.animate(100);
			main.scene.getChild(1).attachChild(temp);
			powerUps.add(temp);
			Debug.d("Power Up got spawned");
		}
		//Spawn the one up when the art exists
		else if(chance > 20 & chance <= 24){
			PowerUp temp = new PowerUp(x, y, main.oneUp,CrowEnum.ONEUP);
			temp.animate(100);
			main.scene.getChild(1).attachChild(temp);
			powerUps.add(temp);
		}
		
	}
	
	protected void spawnSpark(float x, float y){
		HitSpark spark = main.hitSparks.obtainPoolItem();
		spark.animate(200);
		spark.setPosition(x, y);
		if(!spark.hasParent())
			main.scene.getChild(1).attachChild(spark);
		explosions.add(spark);
	}
	
	private void removeEntity(final Entity remove){
		main.runOnUpdateThread(new Runnable() {
            @Override
            public void run() {
            	main.scene.getChild(1).detachChild(remove);
            }
		});
	}
	
	private void recycleEnemy(final Enemy rec){
		String type = rec.getType();
		if(type.compareTo("GRUNT") == 0){
			gruntPool.recyclePoolItem(rec);
		}
		else if(type.compareTo("BLADER") == 0){
			bladerPool.recyclePoolItem(rec);
		}
	}
	
	private void bossExplosion(){
		float height, width, xoff, yoff;
		if(main.currentBoss != null){
			height = main.currentBoss.getHeight();
			width = main.currentBoss.getWidth();
			xoff = main.currentBoss.getX();
			yoff = main.currentBoss.getY();
			main.scene.registerUpdateHandler(new ExplosionTimer(2.5f, 0.25f, new ExplosionCallback(height,width,xoff,yoff)));
		
		}
	}

	// ===========================================================
	// Inner and Anonymous Classes
	// ===========================================================
	
	private class ExplosionTimer extends TimerHandler{
		
		private float duration;
		private float totalElapsedTime;
		
		ExplosionTimer(final float duration, final float interval, final ITimerCallback timerCallback){
			super(interval, true, timerCallback);
			this.duration = duration;
			this.totalElapsedTime = 0;
		}
		
		public void onUpdate(final float delta){
			super.onUpdate(delta);
			this.totalElapsedTime += delta;
			if(this.totalElapsedTime >= this.duration){
				this.setAutoReset(false);
				Debug.d("Finished Exploding");
				if(main.currentBoss.getData().hasCore()){
					for(final Entity core: main.currentBoss.addComps){
						removeEntity(core);
					}
				}
				main.levelDone = true;
				removeEntity(main.currentBoss);
			}
		}
		
	}
	
	private class ExplosionCallback implements ITimerCallback{
		
		private float height;
		private float width;
		private float xOffset;
		private float yOffset;
		
		ExplosionCallback(final float height, final float width, final float xOffset, final float yOffset){
			this.height = height;
			this.width = width;
			this.xOffset =xOffset;
			this.yOffset = yOffset;
		}
		
		@Override
		public void onTimePassed(TimerHandler th) {
			Debug.d("Explosion");
			spawnSpark((rng.nextFloat()*width)+xOffset, (rng.nextFloat()*height)+yOffset);			
		}
		
	}
	
}
